package main

import (
	"errors"
	"fmt"
)

/**
环形队列
*/
type CircelQueue struct {
	head int           //队头指针
	tail int           //队尾指针
	size int           //队列最大容量
	data []interface{} //队列数据存储区域
}

/**
入队
*/
func (this *CircelQueue) Enqueue(value interface{}) error {
	//判断是否队满
	if (this.tail+1)%this.size == this.head {
		return errors.New("queue full")
	}
	//移动指针
	this.tail = (this.tail + 1) % this.size
	//fmt.Println("入队tail:", this.tail)
	//数据入队
	this.data[this.tail] = value
	return nil
}

/**
出队
*/
func (this *CircelQueue) Dequeue() (interface{}, error) {
	//判断是否队空
	if this.tail == this.head {
		return nil, errors.New("queue empty")
	}
	this.head = (this.head + 1) % this.size
	value := this.data[this.head]
	return value, nil
}

/**
显示队头到队尾的数据
*/
func (this *CircelQueue) Showqueue() {
	//求当队列中的元素的个数
	//count := (this.tail - this.head + this.size) % this.size
	/*
		显示环形队列需要考虑到 head 在 tail后面的问题，比如
		head=2 tail =1
		但是不管head是否在 tail的后面还是前面，永远遵循【先进先出】的原则
		和出队的逻辑是一样的，但是不同的是，出队是真实的移动head指针
		因此这里只能 模拟下 head移动指针的动作
	*/
	TempHead := (this.head + 1) % this.size
	for {

		if TempHead == this.tail {
			//队列为空了
			return
		}
		fmt.Printf("显示环形队列元素CircelQueue[%d]=%d\n", TempHead, this.data[TempHead])
		//模拟移动head指针
		TempHead = (TempHead + 1) % this.size
	}
}

/**
构造环形队列
*/
func NewCircelQueue(maxsize int) *CircelQueue {
	cqueue := new(CircelQueue)
	cqueue.head = -1
	cqueue.tail = -1
	cqueue.size = maxsize
	cqueue.data = make([]interface{}, maxsize)
	return cqueue
}

func main() {
	cqueue := NewCircelQueue(6)
	fmt.Println(cqueue.Enqueue(0))
	fmt.Println(cqueue.Enqueue(1))
	fmt.Println(cqueue.Enqueue(2))
	fmt.Println(cqueue.Enqueue(3))
	fmt.Println(cqueue.Enqueue(4))
	fmt.Println(cqueue.Enqueue(5))
	cqueue.Showqueue()

}

//this.tail = (this.tail + 1) % this.size 入队
//this.head = (this.head + 1) % this.size 出队

/*

先出两次对，但是你必须要保证 两次都出成功了的，如果出第二次的时候 已经就队空了，就无法出队了嘛
使用一个大小为6的数组保存循环队列Q，若从Q中出队两个元素，并入队一个元素
，此时队尾rear和队头front的值分别为2 和 4 ，则在执行这三个操作之前
rear 和 front 的值 分别是多少
*/
